"""抖音开放平台API适配器实现"""

import time
import hashlib
import hmac
from typing import Dict, Any, Optional, List, Union
from urllib.parse import urlencode
from ..common.base import BaseAPI
from .exceptions import (
    DouYinError,
    DouYinAccessTokenError,
    DouYinAuthError,
    DouYinRateLimitError
)


class DouYinAPI(BaseAPI):
    """
    抖音开放平台API适配器
    支持抖音小程序、企业号等平台的API调用
    """
    
    def __init__(self,
                 client_key: str,
                 client_secret: str,
                 base_url: str = "https://open.douyin.com",
                 api_base_url: str = "https://open.douyin.com",
                 timeout: int = 30,
                 max_retries: int = 3,
                 retry_delay: int = 1):
        """
        初始化抖音API适配器
        
        Args:
            client_key: 抖音开放平台的Client Key
            client_secret: 抖音开放平台的Client Secret
            base_url: OAuth认证基础URL
            api_base_url: API调用基础URL
            timeout: 请求超时时间（秒）
            max_retries: 最大重试次数
            retry_delay: 重试间隔（秒）
        """
        # 使用api_base_url作为主要请求URL
        super().__init__(api_base_url, timeout, max_retries, retry_delay)
        self.client_key = client_key
        self.client_secret = client_secret
        self.base_url = base_url.rstrip('/')
        self._access_token = None
        self._access_token_expire_time = 0
        self._refresh_token = None
    
    def _handle_response(self, response: 'requests.Response') -> Dict[str, Any]:
        """
        处理抖音API响应，特定处理抖音的错误码
        
        Args:
            response: requests.Response对象
            
        Returns:
            解析后的响应数据
            
        Raises:
            DouYinError: 抖音API调用错误
        """
        try:
            data = response.json()
        except ValueError:
            # 非JSON响应
            data = {"content": response.text}
        
        # 抖音API特有的错误处理（根据抖音开放平台规范）
        # 检查是否有error字段
        if "error" in data:
            error_info = data["error"]
            err_no = error_info.get("error_code", error_info.get("errno"))
            err_msg = error_info.get("error_msg", error_info.get("message", "未知错误"))
            
            # 特定错误码处理
            if err_no == 40001:
                # AccessToken无效或已过期
                self._access_token = None
                self._access_token_expire_time = 0
                raise DouYinAccessTokenError(f"AccessToken错误: {err_msg}", err_no, response)
            elif err_no == 429:
                # API调用频率限制
                raise DouYinRateLimitError(f"API调用频率限制: {err_msg}", err_no, response)
            elif err_no in [40002, 40013, 41001, 41002, 41003]:
                # 认证相关错误
                raise DouYinAuthError(f"认证错误: {err_msg}", err_no, response)
            else:
                # 其他抖音错误
                raise DouYinError(f"抖音API错误: {err_msg}", err_no, response)
        
        # 检查是否有errno字段（抖音某些API使用这种格式）
        elif "errno" in data and data["errno"] != 0:
            err_no = data["errno"]
            err_msg = data.get("message", data.get("errmsg", "未知错误"))
            
            # 特定错误码处理
            if err_no == 40001:
                self._access_token = None
                self._access_token_expire_time = 0
                raise DouYinAccessTokenError(f"AccessToken错误: {err_msg}", err_no, response)
            elif err_no == 429:
                raise DouYinRateLimitError(f"API调用频率限制: {err_msg}", err_no, response)
            else:
                raise DouYinError(f"抖音API错误: {err_msg}", err_no, response)
        
        # HTTP状态码错误处理
        if response.status_code >= 400:
            super()._handle_response(response)
        
        return data
    
    def get_authorize_url(self, redirect_uri: str, scope: str = "", state: str = "") -> str:
        """
        获取OAuth2.0授权URL
        
        Args:
            redirect_uri: 授权回调地址
            scope: 请求的权限范围
            state: 状态参数，用于防止CSRF攻击
            
        Returns:
            授权URL
        """
        params = {
            "client_key": self.client_key,
            "response_type": "code",
            "redirect_uri": redirect_uri
        }
        
        if scope:
            params["scope"] = scope
        if state:
            params["state"] = state
        
        return f"{self.base_url}/platform/oauth/connect?{urlencode(params)}"
    
    def get_access_token(self, code: str = None, force_refresh: bool = False) -> Dict[str, Any]:
        """
        获取或刷新AccessToken
        
        Args:
            code: OAuth授权码，用于首次获取token
            force_refresh: 是否强制刷新
            
        Returns:
            包含access_token等信息的字典
            
        Raises:
            DouYinAccessTokenError: AccessToken获取失败
        """
        # 检查AccessToken是否存在且未过期
        current_time = time.time()
        if (not force_refresh and 
            self._access_token and 
            current_time < self._access_token_expire_time):
            return {
                "access_token": self._access_token,
                "expires_in": int(self._access_token_expire_time - current_time),
                "refresh_token": self._refresh_token
            }
        
        endpoint = "/oauth/access_token/"
        
        if code:
            # 使用授权码获取AccessToken
            data = {
                "client_key": self.client_key,
                "client_secret": self.client_secret,
                "code": code,
                "grant_type": "authorization_code"
            }
        elif self._refresh_token:
            # 使用刷新token获取新的AccessToken
            data = {
                "client_key": self.client_key,
                "client_secret": self.client_secret,
                "refresh_token": self._refresh_token,
                "grant_type": "refresh_token"
            }
        else:
            raise DouYinAccessTokenError("缺少授权码或刷新token")
        
        try:
            response = super().post(endpoint, json_data=data)
            
            # 更新token信息
            self._access_token = response["access_token"]
            self._refresh_token = response.get("refresh_token")
            
            # 计算过期时间，减去10分钟作为缓冲
            expires_in = response.get("expires_in", 7200) - 600
            self._access_token_expire_time = current_time + expires_in
            
            return response
        except Exception as e:
            if not isinstance(e, DouYinAccessTokenError):
                raise DouYinAccessTokenError(f"获取AccessToken失败: {str(e)}") from e
            raise
    
    def _request(self, *args, **kwargs) -> Dict[str, Any]:
        """
        重写请求方法，自动添加AccessToken
        """
        # 确保有headers参数
        if "headers" not in kwargs:
            kwargs["headers"] = {}
        
        # 如果headers中没有Authorization，则自动添加
        if "Authorization" not in kwargs["headers"] and self._access_token:
            kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
        
        try:
            return super()._request(*args, **kwargs)
        except DouYinAccessTokenError:
            # AccessToken失效，尝试刷新并重试
            if self._refresh_token:
                self.get_access_token(force_refresh=True)
                kwargs["headers"]["Authorization"] = f"Bearer {self._access_token}"
                return super()._request(*args, **kwargs)
            raise
    
    # === 用户相关接口 ===
    
    def get_user_info(self, open_id: str, access_token: str = None) -> Dict[str, Any]:
        """
        获取用户信息
        
        Args:
            open_id: 用户的open_id
            access_token: 可选的access_token，默认使用实例中的token
            
        Returns:
            用户信息
        """
        endpoint = "/oauth/userinfo/"
        params = {
            "open_id": open_id,
            "access_token": access_token or self._access_token
        }
        return self.get(endpoint, params=params)
    
    # === 内容管理接口 ===
    
    def create_video(self, 
                    title: str,
                    description: str,
                    video_file: str,
                    cover_file: str = None,
                    tags: List[str] = None) -> Dict[str, Any]:
        """
        上传并发布视频
        
        Args:
            title: 视频标题
            description: 视频描述
            video_file: 视频文件路径
            cover_file: 封面文件路径
            tags: 视频标签列表
            
        Returns:
            上传结果
        """
        # 这里只是示例，实际接口可能有所不同
        # 需要先上传视频文件，再创建视频
        
        # 1. 上传视频文件
        video_media = self.upload_media(video_file, "video")
        video_id = video_media.get("video_id")
        
        # 2. 如果有封面，上传封面文件
        cover_id = None
        if cover_file:
            cover_media = self.upload_media(cover_file, "image")
            cover_id = cover_media.get("image_id")
        
        # 3. 创建视频
        endpoint = "/video/create/"
        data = {
            "title": title,
            "description": description,
            "video_id": video_id
        }
        
        if cover_id:
            data["cover_id"] = cover_id
        if tags:
            data["tags"] = tags
        
        return self.post(endpoint, json_data=data)
    
    def upload_media(self, file_path: str, media_type: str = "video") -> Dict[str, Any]:
        """
        上传媒体文件
        
        Args:
            file_path: 文件路径
            media_type: 媒体类型，可选值：video, image
            
        Returns:
            上传结果
        """
        endpoint = f"/media/upload/?type={media_type}"
        
        with open(file_path, 'rb') as f:
            files = {'media': f}
            return self.post(endpoint, files=files)
    
    # === 互动相关接口 ===
    
    def get_video_comments(self, 
                          item_id: str,
                          cursor: int = 0,
                          count: int = 20) -> Dict[str, Any]:
        """
        获取视频评论列表
        
        Args:
            item_id: 视频ID
            cursor: 分页游标
            count: 每页数量
            
        Returns:
            评论列表
        """
        endpoint = "/comment/list/"
        params = {
            "item_id": item_id,
            "cursor": cursor,
            "count": count
        }
        return self.get(endpoint, params=params)
    
    def reply_comment(self, 
                     item_id: str,
                     comment_id: str,
                     content: str) -> Dict[str, Any]:
        """
        回复评论
        
        Args:
            item_id: 视频ID
            comment_id: 评论ID
            content: 回复内容
            
        Returns:
            操作结果
        """
        endpoint = "/comment/reply/"
        data = {
            "item_id": item_id,
            "comment_id": comment_id,
            "content": content
        }
        return self.post(endpoint, json_data=data)
    
    # === 数据分析接口 ===
    
    def get_user_stat(self, 
                     open_id: str,
                     start_date: str,
                     end_date: str) -> Dict[str, Any]:
        """
        获取用户数据统计
        
        Args:
            open_id: 用户open_id
            start_date: 开始日期，格式：yyyyMMdd
            end_date: 结束日期，格式：yyyyMMdd
            
        Returns:
            统计数据
        """
        endpoint = "/data/extern/user/"
        params = {
            "open_id": open_id,
            "start_date": start_date,
            "end_date": end_date
        }
        return self.get(endpoint, params=params)
    
    def get_video_stat(self, 
                      item_ids: List[str]) -> Dict[str, Any]:
        """
        获取视频数据统计
        
        Args:
            item_ids: 视频ID列表
            
        Returns:
            统计数据
        """
        endpoint = "/data/extern/item/"
        params = {
            "item_ids": ",".join(item_ids)
        }
        return self.get(endpoint, params=params)
    
    # === 小程序相关接口 ===
    
    def get_qrcode(self, 
                  appname: str,
                  page: str = "",
                  query: str = "",
                  width: int = 430) -> bytes:
        """
        获取小程序二维码
        
        Args:
            appname: 小程序名称
            page: 小程序页面路径
            query: 小程序页面参数
            width: 二维码宽度
            
        Returns:
            二维码图片二进制数据
        """
        endpoint = "/api/apps/qrcode"
        data = {
            "appname": appname,
            "page": page,
            "query": query,
            "width": width
        }
        
        # 需要特殊处理二进制响应
        response = self.session.post(
            f"{self.base_url}{endpoint}",
            json=data,
            headers={"Authorization": f"Bearer {self._access_token}"},
            timeout=self.timeout
        )
        
        # 检查是否是图片响应
        if response.headers.get('Content-Type', '').startswith('image/'):
            return response.content
        else:
            # 尝试解析错误信息
            try:
                error_data = response.json()
                raise DouYinError(
                    f"获取二维码失败: {error_data.get('message', '未知错误')}",
                    error_data.get('errno')
                )
            except ValueError:
                raise DouYinError(f"获取二维码失败: {response.text}")
    
    # === 企业号相关接口 ===
    
    def create_enterprise_message(self, 
                                touser: str,
                                msgtype: str,
                                message_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        发送企业消息
        
        Args:
            touser: 接收者的open_id或user_id
            msgtype: 消息类型
            message_data: 消息内容数据
            
        Returns:
            发送结果
        """
        endpoint = "/message/send/"
        data = {
            "touser": touser,
            "msgtype": msgtype
        }
        
        # 添加对应类型的消息内容
        data.update(message_data)
        
        return self.post(endpoint, json_data=data)